package cn.maihe.elg.operation.centers.guogu.handler;

import cn.maihe.elg.operation.centers.guogu.auth.GuoGuCenterAuthConfig;
import cn.maihe.elg.operation.centers.guogu.config.GuoGuCenterInfosConfig;
import cn.maihe.elg.operation.centers.guogu.dto.GuoGuBaseReqDTO;
import cn.maihe.elg.operation.centers.guogu.dto.GuoGuBaseRespDTO;
import cn.maihe.elg.operation.centers.guogu.enums.GuoGuRespCodeEnum;
import cn.maihe.elg.operation.centers.guogu.exception.GuoGuException;
import cn.maihe.elg.operation.centers.supports.BaseCenterService;
import cn.maihe.elg.operation.centers.supports.CenterTradeContext;
import cn.maihe.elg.operation.model.enums.CenterNoEnum;
import cn.maihe.elg.operation.utils.ElgLogger;
import cn.maihe.elg.operation.utils.ValidateUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;

/**
 * @Description
 * @Author SSX
 * @Date 2025/03/18
 */
@Slf4j
public abstract class GuoGuCenterTradeService<REQ extends GuoGuBaseReqDTO, RESP extends GuoGuBaseRespDTO> extends BaseCenterService<REQ, RESP> {

    protected final GuoGuCenterInfosConfig centerInfosConfig;
    protected static final String authConfigKey = "authConfigKey";

    public GuoGuCenterTradeService(GuoGuCenterInfosConfig centerInfosConfig) {
        super();
        this.centerInfosConfig = centerInfosConfig;
    }


    protected GuoGuCenterAuthConfig getCurAuthConfig(CenterTradeContext<REQ, RESP> context) {
        return (GuoGuCenterAuthConfig) context.getAttr(authConfigKey);
    }

    public GuoGuCenterInfosConfig.CenterInfo getCurCenterInfo(CenterTradeContext<REQ, RESP> context) {
        return getCurAuthConfig(context).getCenterInfo();
    }

    /**
     * @param bidBond    保证金单位分
     * @param feeRate    保费费率 默认百分几 ： 三明feeRate默认0.15%，保底300，上不封顶
     * @param minFeeYuan 保底300，上不封顶
     * @return 保费 单位分
     */
    public Long calcGuaranteeFee(Long bidBond, String feeRate, Long minFeeYuan) {
        if (bidBond == null || StringUtils.isBlank(feeRate)) {
            throw new RuntimeException("保费计算异常:保证金为空或费率为空");
        }
        //0.01% 转0.0001
        BigDecimal rate = new BigDecimal(feeRate).divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP);

        BigDecimal guaranteeFee = new BigDecimal(bidBond).multiply(rate);
        Long guaranteeFeeFen = guaranteeFee.longValue();
        long minFeeFen = minFeeYuan * 100;
        if (guaranteeFeeFen < minFeeFen) {
            guaranteeFeeFen = minFeeFen;
        }
        return guaranteeFeeFen;
    }

    public Class<REQ> getReqClass() {
        return reqClass;
    }

    public Class<RESP> getRespClass() {
        return respClass;
    }

    /**
     * 国谷保函处理
     *
     * @param centerNoEnum
     * @param orgCode
     * @param reqParams
     * @param attrMap      附加属性值
     * @return
     */
    @Override
    public RESP doService(CenterNoEnum centerNoEnum, String orgCode, Map<String, Object> reqParams, Map<String, Object> attrMap) {
        ElgLogger.info(log, log -> log.info("[{}:{}][{}] => 原始请求参数:{} 附属属性:{}", centerNoEnum.getCenterNo(), orgCode, tradeService.tradeType(), toJsonString(reqParams), toJsonString(attrMap)));
        GuoGuCenterAuthConfig authConfig = null;
        try {
            authConfig = this.getAuthConfig(centerNoEnum);
        } catch (Exception e) {
            throw GuoGuException.of(GuoGuRespCodeEnum.FAILURE, "参数未配置:" + e.getMessage(), e);
        }

        //反序列化
        REQ req = this.buildReq(reqParams);

        ElgLogger.info(log, log -> log.info("解密完请求报文:{}", toJsonString(req)));

        //上下文
        CenterTradeContext context = buildCenterTradeContext(req, centerNoEnum, orgCode, attrMap);
        context.setAttr(authConfigKey, authConfig);

        //参数格式规范校验
        verifyParam(context);

        RESP resp = null;
        try {
            resp = (RESP) processService(context);
            context.setResp(resp);
        } catch (Exception e) {
            if (e instanceof GuoGuException) {
                throw e;
            }
            throw GuoGuException.error(e.getMessage(), e);
        }
        // 响应报文无需签名&加密
        return resp;
    }

    private CenterTradeContext buildCenterTradeContext(REQ req, CenterNoEnum centerNoEnum, String orgCode, Map<String, Object> attrMap) {
        CenterTradeContext context = new CenterTradeContext(req, centerNoEnum.getCenterNo(), orgCode, getCurCenterId(centerNoEnum), getOrgId(orgCode));
        context.putAll(attrMap);
        return context;
    }

    protected GuoGuCenterAuthConfig getAuthConfig(CenterNoEnum centerNoEnum) {
        GuoGuCenterAuthConfig authConfig = centerInfosConfig.getAuthConfig(centerNoEnum);
        Assert.notNull(authConfig, () -> centerNoEnum + " 认证相关参数未配置");
        Assert.notNull(authConfig.getCenterInfo(), () -> centerNoEnum + " 认证相关参数未配置");
        Assert.notNull(authConfig.getGuoGuAuthorization(), () -> centerNoEnum + " 签名验签相关参数未配置");
        Assert.notNull(authConfig.getGuoGuEncryption(), () -> centerNoEnum + " 加解密相关参数未配置");
        return authConfig;
    }


    protected boolean verifySignature(GuoGuCenterAuthConfig authConfig, Map<String, Object> req) {
        try {
            return authConfig.getGuoGuAuthorization().verifySignature(req, (String) req.get("sign"), (String) req.get("timestamp"));
        } catch (Exception e) {
            throw GuoGuException.error("验签失败:" + e.getMessage(), e);
        }
    }

    /**
     * 具体业务处理
     *
     * @param context
     */
    public abstract RESP processService(CenterTradeContext<REQ, RESP> context);

    /**
     * 参数校验：参数规范校验，涉及业务需落库的后置processService处理
     *
     * @param context
     */
    protected void verifyParam(CenterTradeContext<REQ, RESP> context) {
        try {
            ValidateUtil.validate(context.getReq());
        } catch (Exception e) {
            throw GuoGuException.of(GuoGuRespCodeEnum.FAILURE, "参数校验失败:" + e.getMessage(), e);
        }
    }

    protected REQ buildReq(Map<String, Object> paramMap) {
        return JSON.parseObject(JSON.toJSONString(paramMap), this.reqClass);
    }


}
